package com.sunseagear.wind.modules.sso.service.impl;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunseagear.common.base.exception.BaseException;
import com.sunseagear.common.base.satoken.entity.LoginUser;
import com.sunseagear.common.utils.CacheUtils;
import com.sunseagear.common.utils.StreamUtils;
import com.sunseagear.common.utils.StringUtils;
import com.sunseagear.wind.common.helper.SysConfigHelper;
import com.sunseagear.wind.modules.sso.entity.bo.LoginBo;
import com.sunseagear.wind.modules.sso.service.IOAuthService;
import com.sunseagear.wind.modules.sys.entity.SysConfig;
import com.sunseagear.wind.modules.sys.entity.User;
import com.sunseagear.wind.modules.sys.service.IMenuService;
import com.sunseagear.wind.modules.sys.service.IUserService;
import com.sunseagear.wind.common.utils.LoginLogUtils;
import com.sunseagear.wind.common.utils.UserUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 *
 * @version V1.0
 * @package com.sunseagear.sso.server.service.impl
 * @title:
 * @description: 单点登录 * @date: 2018/3/29 9:30
 * @copyright: 2017 www.sunseagear.com Inc. All rights reserved.
 */
@Slf4j
@Service("oAuthService")
@RequiredArgsConstructor
public class OAuthServiceImpl implements IOAuthService {

    private final IUserService userService;
    private final IMenuService menuService;


    private final String PREFIX = "";
    // 如果要求服务器重启后所有用户全部重新登录，则采用这种配置。
//    private final String PREFIX = UserUtils.USER_CACHE + ":";


    private final String ACCESS_TOKEN = PREFIX + "access_token:";


    /**
     * 校验token是否有效
     *
     * @param accessToken token令牌
     */
    @Override
    public void revokeToken(String accessToken) {
        try {
            LoginUser loginUser = UserUtils.getLoginUser();
            if (ObjectUtil.isNull(loginUser)) {
                return;
            }
        } catch (NotLoginException ignored) {
        } finally {
            try {
                StpUtil.logout();
            } catch (NotLoginException ignored) {
            }
        }
        CacheUtils.valueDelete(ACCESS_TOKEN + accessToken);
    }


    /**
     * 构建登录用户
     *
     * @param user 用户
     * @return 登录用户对象
     */
    private LoginUser buildLoginUser(User user) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getId());
        loginUser.setUsername(user.getUsername());
        loginUser.setRealname(user.getRealname());
        loginUser.setTenantId(user.getTenantId());

        loginUser.setLoginTime(System.currentTimeMillis());
        SysConfig tokenExpire = SysConfigHelper.getInstance().getSysConfig("tokenExpire");
        loginUser.setExpireTime(loginUser.getLoginTime() +  Long.parseLong(tokenExpire.getValue()) * 1000);

        loginUser.setRoles(Collections.singletonList(user.getRoles()));

        return loginUser;
    }

    /**
     * 密码登录
     *
     * @param loginBo 登录参数
     */
    @Override
    public LoginUser passwordAuthLogin(LoginBo loginBo) {
        User user = userService.loadUserByUsername(loginBo.getUsername());
        if (Objects.isNull(user)) {
            throw new BaseException("用户不存在");
        }
        if (!user.getStatus().equals("STATUS_NORMAL")) {
            LoginLogUtils.recordFailLoginLog(user.getUsername(), "用户无法登陆");
            throw new BaseException("用户无法登陆");
        }
        if (!this.checkPassword(user.getPassword(), loginBo.getPassword())) {
            LoginLogUtils.recordFailLoginLog(user.getUsername(), "密码错误");
            throw new BaseException("密码错误");
        }

        LoginUser loginUser = buildLoginUser(user);
        loginUser.setDeviceType(loginBo.getClientType());

        // 生成token
        UserUtils.login(loginUser);

        return loginUser;
    }


    /**
     * 校验密码
     *
     * @param password     数据库加密密码
     * @param formPassword 提交加的密码
     */
    private boolean checkPassword(String password, String formPassword) {
        return BCrypt.checkpw(formPassword, password);
    }

    /**
     * 查询当前活跃用户
     *
     * @param ipaddr   ip地址
     * @param userName 用户名
     * @return 登录人信息
     */
    @Override
    public List<LoginUser> activeLoginUser(Page<LoginUser> page, String ipaddr, String userName) {


        // 获取所有未过期的 token
        List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
        List<LoginUser> userOnlineDTOList = new ArrayList<>();
        for (String key : keys) {
            String token = StringUtils.substringAfterLast(key, ":");
            // 如果已经过期则跳过
            if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) {
                continue;
            }
            userOnlineDTOList.add(CacheUtils.valueGet(UserUtils.ONLINE_TOKEN_KEY + token));
        }
        if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> {

                if (Objects.isNull(userOnline)) {
                    return false;
                }
                return StringUtils.contains(userOnline.getIpaddr(),ipaddr) &&
                        StringUtils.contains(userOnline.getRealname(),userName);
            });
        } else if (StringUtils.isNotEmpty(ipaddr)) {
            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> {
                if (Objects.isNull(userOnline)) {
                    return false;
                }
                return StringUtils.contains( userOnline.getIpaddr(),ipaddr);
            });

        } else if (StringUtils.isNotEmpty(userName)) {
            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> {
                if (Objects.isNull(userOnline)) {
                    return false;
                }
                return StringUtils.contains(userOnline.getRealname(),userName);
            });
        }
        Collections.reverse(userOnlineDTOList);
        userOnlineDTOList.removeAll(Collections.singleton(null));
        List<LoginUser> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, LoginUser.class);
        return userOnlineList;
    }
}
